package org.example.wx.client.connection;

import io.netty.channel.socket.SocketChannel;
import org.apache.zookeeper.KeeperException;
import org.example.wx.Node.Interface.ServiceRegister;
import org.example.wx.Node.ZooKeeperServer;
import org.example.wx.client.loadBalance.LoadBalancer;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static org.example.wx.client.loadBalance.LoadBalanceFactory.getLoadBalancer;

/**
 * 用来管理各个服务名和对应的socketChannel，
 */
public class ConnectionManager {

    private static final LoadBalancer loadBalancer = getLoadBalancer("polling");

    private static Map<ServiceInfo, SocketChannel> socketChannelMap = new ConcurrentHashMap<ServiceInfo, SocketChannel>();

    private static ServiceRegister serviceRegister = new ZooKeeperServer(111);

    /**
     * 将服务对应的链接写入到socketChannelMap当中
     *
     * @param serviceInfo
     * @param socketChannel
     */
    static void regist(ServiceInfo serviceInfo, SocketChannel socketChannel) {
        socketChannelMap.put(serviceInfo, socketChannel);
    }

    /**
     * 将失去链接的服务从socketChannelMap删除
     *
     * @param serviceInfo
     */
    static void deregist(ServiceInfo serviceInfo) {
        socketChannelMap.remove(serviceInfo);
    }

    /**
     * 根据服务的名称获取对应的链接（负载均衡决定使用哪个链接）
     *
     * @param serviceName
     * @return
     */
    public static SocketChannel getConnectionByServiceName(final String serviceName) throws KeeperException, InterruptedException {
        refresh(serviceName);
        List<ServiceInfo> serviceInfos = socketChannelMap.keySet().stream().filter(serviceInfo -> serviceInfo.getServiceName().equals(serviceName)).collect(Collectors.toList());
        ServiceInfo choosenServiceInfo = loadBalancer.choose(serviceInfos);
        SocketChannel channel = socketChannelMap.get(choosenServiceInfo);
        if (channel == null) {
            //如果channel为空，则需要创建
            channel = createChannel(choosenServiceInfo);

        }
        return channel;

    }

    /**
     * 更新服务对应的链接
     */
    static void refresh(String serviceName) throws KeeperException, InterruptedException {
        List<String> serviceInfos = serviceRegister.refreshServiceInfos(serviceName);
        List<ServiceInfo> lastServiceInfos = serviceInfos.stream().map(info -> {
            String ipAddress = info.split(":")[0];
            String port = info.split(":")[1];
            ServiceInfo serviceInfo = new ServiceInfo();
            serviceInfo.setIpAddress(ipAddress);
            serviceInfo.setPort(Integer.valueOf(port));
            serviceInfo.setServiceName(serviceName);
            return serviceInfo;
        }).collect(Collectors.toList());

        List<ServiceInfo> originalServiceInfos = socketChannelMap.keySet().stream().filter(serviceInfo -> serviceName.equals(serviceInfo.getServiceName())).collect(Collectors.toList());

        //失效的链接，需要被删除
        List<ServiceInfo> toBeRemoved = originalServiceInfos.stream().filter(serviceInfo -> !lastServiceInfos.contains(serviceInfo)).collect(Collectors.toList());
        //新增的链接，需要添加
        List<ServiceInfo> toBeAdded = lastServiceInfos.stream().filter(serviceInfo -> originalServiceInfos.contains(serviceInfo)).collect(Collectors.toList());

        //不在这里创建链接，只有在第一次使用时创建
        toBeAdded.forEach(added -> socketChannelMap.put(added, null));
        toBeRemoved.forEach(moved -> {
            SocketChannel socketChannel = socketChannelMap.remove(moved);
            closeChannel(socketChannel);
        });
    }

    /**
     * 根据ip和port创建链接，注册到客户端BootStrap当中
     *
     * @param serviceInfo
     * @return
     */
    static SocketChannel createChannel(ServiceInfo serviceInfo) {
        SocketChannel channel = null;

        //    TODO:这里需要根据ip和port创建链接，并且注册到客户端的BootStrap当中


        //将channel注册进ConnectionManager的缓存
        socketChannelMap.replace(serviceInfo, channel);
        return null;
    }

    /**
     * 关闭链接，并且从BootStrap当中注销
     *
     * @param channel
     */
    static void closeChannel(SocketChannel channel) {
//    TODO:关闭链接，并且从BootStrap当中注销
    }

}
